package com.zb.aop;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * AspectJ使用org.aspectj.lang.JoinPoint接口表示目标类连接点对象
	如果是环绕增强时，使用org.aspectj.lang.ProceedingJoinPoint表示连接点对象，该类是JoinPoint的子接口。
	任何一个增强方法都可以通过将第一个入参声明为JoinPoint访问到连接点上下文的信息。我们先来了解一下这两个接口的主要方法： 
	1)JoinPoint 
		java.lang.Object[] getArgs()：获取连接点方法运行时的入参列表； 
 			Signature getSignature() ：获取连接点的方法签名对象； 
		java.lang.Object getTarget() ：获取连接点所在的目标对象； 
		java.lang.Object getThis() ：获取代理对象本身； 
	2)ProceedingJoinPoint 
		ProceedingJoinPoint继承JoinPoint子接口，它新增了两个用于执行连接点方法的方法： 
		java.lang.Object proceed() throws java.lang.Throwable：通过反射执行目标对象的连接点处的方法； 
		java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable：通过反射执行目标对象连接点处的方法，不过使用新的入参替换原来的入参。
 */
/**
 * AOP请求捕捉
 * 
 * @author zhoubang
 * 创建时间 : 2014年8月26日 下午1:15:09
 *
 */
//声明这是一个组件
//声明这是一个切面Bean
@Aspect
@Component
public class AOPLoggerAspect {
    
    private  static Logger logger = Logger.getLogger(AOPLoggerAspect.class);
    
    /**
     * 
     * <strong>Description:<br/></strong>
     * 配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
     * @author zhoubang
     * 创建时间：2014年8月26日 下午1:27:24
     */
    @Pointcut("execution(* com.zb..*(..))")
    public void aspect(){}
    
    
    /**
     * 
     * <strong>Description:<br/></strong>
     * 配置前置通知,使用在方法aspect()上注册的切入点
     * 同时接受JoinPoint切入点对象,可以没有该参数
     *
     * @author zhoubang
     * 创建时间：2014年8月26日 下午1:28:57
     */
    @Before("aspect()")
    public void before(JoinPoint joinPoint){
        if(logger.isInfoEnabled()){
            logger.info("before " + joinPoint);
            }
        }
    
    
    /**
     * 
     * <strong>Description:<br/></strong>
     * 配置后置通知,使用在方法aspect()上注册的切入点
     *
     * @author zhoubang
     * 创建时间：2014年8月26日 下午1:32:11
     */
    @After("aspect()")
    public void after(JoinPoint joinPoint){
        if(logger.isInfoEnabled()){
            logger.info("after " + joinPoint);
            }
        }
    
    
    /**
     * 
     * <strong>Description:<br/></strong>
     * 配置环绕通知,使用在方法aspect()上注册的切入点<br/><br/>
     * 输入请求的对象信息:如：方法参数列表信息、类名称等<br/><br/>
     * 注意：参数必须使用ProceedingJoinPoint，否则被拦截的控制台方法将不执行.<br/><br/>
     * 只有环绕通知才可以使用JoinPoint的子类ProceedingJoinPoint.<br/><br/>
     * 环绕方法通知要注意必须给出调用之后的返回值，否则被代理的方法会停止调用并返回null<br/><br/>
     * @author zhoubang
     * 创建时间：2014年8月26日 下午1:33:27
     * @throws Throwable 
     */
    @Around("aspect()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        //获取类的完全限定名
        String className = joinPoint.getSignature().getDeclaringTypeName();
        //反射获取该类的对象
        Class<?> cls = Class.forName(className);
        //获取执行的方法名称
        String methodName = joinPoint.getSignature().getName();
        
        //打印输出参数信息
        StringBuffer logContent = new StringBuffer();
        logContent.append("\r\n\n");
        logContent.append("------------------------");
        logContent.append(cls.getSimpleName() + "中的" + methodName + "方法被调用");
        logContent.append("------------------------");
        logContent.append("\r\n");
        logContent.append("参数如下：");
        
        //获取参数列表
        Object[] args = joinPoint.getArgs();
        if(args != null){
            if(args.length > 0){
                //遍历参数列表
                for (int i = 0; i < args.length; i++) {
                    if(args[i] != null){
                        logContent.append("\n第" + (i + 1) + "个参数值：" + args[i]);
                    //输出参数的值
                    }else{
                        logContent.append("\n第" + (i + 1) + "个参数值：" + args[i]);
                    }
                }
            }else{
                //无参方法被调用
                logContent.append("\n" + methodName + "方法为无参方法。");
            }
        }
        logContent.append("\n");
        logger.info(logContent);
        //目标对象执行
        return joinPoint.proceed(args);
    }
    
    
    /**
     * 
     * <strong>Description:<br/></strong>
     * 配置后置返回通知,使用在方法aspect()上注册的切入点
     *
     * @author zhoubang
     * 创建时间：2014年8月26日 下午1:34:47
     */
    @AfterReturning("aspect()")
    public void afterReturn(JoinPoint joinPoint){
        if(logger.isInfoEnabled()){
            logger.info("afterReturn " + joinPoint);
        }
    }
    
    
    @AfterThrowing(pointcut="aspect()", throwing="ex")
    public void afterThrow(JoinPoint joinPoint,Exception ex) {
        if(logger.isInfoEnabled()){
            logger.info("afterThrow " + joinPoint + "\t" + ex.getMessage());
        }
    }
    
    
    
}
